package controllers

import org.joda.time.DateTime
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}

import java.io.File
import javax.inject.Inject
import dao._
import implicits.Implicits._
import models.Tables._
import net.ruippeixotog.scalascraper.browser.JsoupBrowser
import net.ruippeixotog.scalascraper.dsl.DSL._
import net.ruippeixotog.scalascraper.dsl.DSL.Extract._
import net.ruippeixotog.scalascraper.dsl.DSL.Parse._
import net.ruippeixotog.scalascraper.model._
import org.joda.time.format.DateTimeFormat
import upickle.default._
import utils.Utils

import scala.collection.immutable.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global

/** Created by Administrator on 2019/8/14
  */
class DataInsertController @Inject() (
    cc: ControllerComponents,
    implicit val prokaryoteDao: ProkaryoteDao,
    implicit val bioSampleDao: BioSampleDao,
    implicit val assemblyDao: AssemblyDao,
    implicit val snpDao: SnpDao,
    implicit val cdsSnpDao: CdsSnpDao,
    implicit val cdsInDelDao: CdsInDelDao,
    implicit val interInDelDao: InterInDelDao
) extends AbstractController(cc) {

  val parentDir = new File("E:\\mtd_database\\test")

  // 20230815
  def insertBrowse = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "prokaryotes.csv")
    val maps = file.csvLines.lineSeqMap
    val rows = maps.map { map =>
      ProkaryoteRow(
        organismInfraspecificNamesStrain = map("Organism Infraspecific Names Strain"),
        bioSample = map("BioSample"),
        assembly = map("Assembly Accession"),
        assemblyLevel = map("Assembly Level").trim,
        assemblyStatsTotalSequenceLength = map("Assembly Stats Total Sequence Length").toDouble,
        assemblyReleaseDate = DateTime.parse(map("Assembly Release Date"), DateTimeFormat.forPattern("yyyy/M/d")),
        assemblyName = map("Assembly Name"),
        organismName = map("Organism Name"),
        organismInfraspecificNamesBreed = map("Organism Infraspecific Names Breed"),
        organismInfraspecificNamesCultivar = map("Organism Infraspecific Names Cultivar"),
        organismInfraspecificNamesEcotype = map("Organism Infraspecific Names Ecotype"),
        organismInfraspecificNamesIsolate = map("Organism Infraspecific Names Isolate"),
        organismInfraspecificNamesSex = map("Organism Infraspecific Names Sex"),
        annotationName = map("Annotation Name")
      )
    }
    prokaryoteDao.deleteAll.flatMap(_ => prokaryoteDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  // 20230815
  def insertBioSample = Action.async {
    val startTime = System.currentTimeMillis()
    val listFile = new File(parentDir, "list.txt")
    val outDir = new File(parentDir, "anno")
    val ids = listFile.lines
    val rows = ids.map { id =>
      val file = new File(outDir, s"${id}.txt")
      val attributes = file.lines
        .map(_.trim)
        .filter(_.startsWith("/"))
        .map { line =>
          val r = "^/(.*)=\"(.*)\"$".r
          val matcher = r.findAllIn(line)
          (matcher.group(1), matcher.group(2))
        }
        .toSeqMap
      BioSampleRow(
        bioSample = id,
        attributes = writeJs(attributes)
      )
    }
    bioSampleDao.deleteAll.flatMap(_ => bioSampleDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  implicit def seqMapReadTWrite[T](implicit writer: Writer[T], reader: Reader[T]): ReadWriter[SeqMap[String, T]] = {
    readwriter[ujson.Obj].bimap[SeqMap[String, T]](x => {
      val ts = x.toList.map { case (k, v) =>
        (k, writeJs(v))
      }
      ujson.Obj.from(ts)
    }, y => {
      val ts = y.value.map { case (k, v) =>
        (k, read[T](v))
      }
      SeqMap.from(ts)
    })
  }

  // 20230816
  def insertAssembly = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "prokaryotes.csv")
    val maps = file.csvLines.lineSeqMap
    val outDir = new File(parentDir, "assembly_full_report")
    val ids = maps.map(_("Assembly"))
    val rows = ids.map { id =>
      val file = new File(outDir, s"${id}.html")
      val browser = JsoupBrowser()
      val doc = browser.parseFile(file)
      val summaryEle = doc >> element("div#summary")
      val dts = (summaryEle >> texts("dt")).map { x =>
        x.replaceAll(":$", "")
      }
      val dds = (summaryEle >> elements("dd")).toList
      val summary = dts
        .zip(dds)
        .toList
        .map { case (k, ele) =>
          val dd = if (k == "WGS Project") {
            (ele >?> element("a"))
              .map { x =>
                val link = x.attr("href").split("/").last
                s"${ele.text}(${link})"
              }
              .getOrElse {
                ele.text
              }
          } else ele.text
          (k, dd)
        }
        .toSeqMap
      val name = summaryEle >> text("h1")
      val annoEleOp = doc >?> element("div.st-comment-1")
      val infos = annoEleOp
        .map { annoEle =>
          (annoEle >> texts("span"))
            .grouped(3)
            .map { spans =>
              val spanList = spans.toList
              (spanList(0), spanList(2))
            }
            .toList
            .toSeqMap
        }
        .getOrElse {
          SeqMap[String, String]()
        }
      AssemblyRow(
        assembly = id,
        name = name,
        summary = writeJs(summary),
        genomeAnnoData = writeJs(infos)
      )
    }
    assemblyDao.deleteAll.flatMap(_ => assemblyDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  // 20230905
  def insertSnp = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "inter_snp.txt")
    val maps = file.txtLines.lineSeqMap
    val rows = maps
      .map { map =>
        SnpRow(
          refId = map("ref_id"),
          position = map("position").toInt,
          sampleBase = map("sample_base"),
          mutSampleNum = map("mut_sample_num").toInt,
          refBase = map("ref_base"),
          geneDis = map("gene_dis"),
          geneStart = map("gene_start"),
          geneEnd = map("gene_end"),
          geneStand = map("gene_stand"),
          geneName = map("gene_name"),
          bioSample = map("BioSample")
        )
      }
      .sortBy(x => (x.refId, x.position, x.sampleBase))
    snpDao.deleteAll.flatMap(_ => snpDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  // 20230905
  def insertCdsSnp = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "cds_snp.txt")
    val maps = file.txtLines.lineSeqMap
    val rows = maps
      .map { map =>
        CdsSnpRow(
          refId = map("ref_id"),
          position = map("position").toInt,
          sampleBase = map("sample_base"),
          mutSampleNum = map("mut_sample_num").toInt,
          refBase = map("ref_base"),
          snpStatus = map("snp_status"),
          strand = map("strand"),
          codonPhase = map("codon_phase"),
          codonMutate = map("codon_mutate"),
          aaMutate = map("aa_mutate"),
          mutateType = map("mutate_type"),
          synonymous = map("synonymous"),
          nonsynonymous = map("nonsynonymous"),
          geneId = map("gene_id"),
          posStart = map("pos_start"),
          posEnd = map("pos_end"),
          bioSample = map("BioSample")
        )
      }
      .sortBy(x => (x.refId, x.position, x.sampleBase))
    cdsSnpDao.deleteAll.flatMap(_ => cdsSnpDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  // 20230905
  def insertCdsInDel = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "cds_indel.txt")
    val maps = file.txtLines.lineSeqMap
    val rows = maps
      .map { map =>
        CdsIndelRow(
          ref = map("ref"),
          refStart = map("ref_start").toInt,
          refEnd = map("ref_end").toInt,
          base = map("Base"),
          mutSampleNum = map("mut_sample_num").toInt,
          posType = map("Pos_type"),
          posStart = map("pos_start"),
          posEnd = map("pos_end"),
          strand = map("strand"),
          geneId = map("gene_id"),
          bioSample = map("BioSample")
        )
      }
      .sortBy(x => (x.ref, x.posStart, x.posEnd, x.base))
    cdsInDelDao.deleteAll.flatMap(_ => cdsInDelDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

  // 20230905
  def insertInterInDel = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "inter_inDel.txt")
    val maps = file.txtLines.lineSeqMap
    val rows = maps
      .map { map =>
        InterIndelRow(
          ref = map("ref"),
          refStart = map("ref_start").toInt,
          refEnd = map("ref_end").toInt,
          base = map("Base"),
          mutSampleNum = map("mut_sample_num").toInt,
          geneDis = map("gene_dis"),
          posStart = map("pos_start"),
          posEnd = map("pos_end"),
          strand = map("strand"),
          geneId = map("gene_id"),
          bioSample = map("BioSample")
        )
      }
      .sortBy(x => (x.ref, x.posStart, x.posEnd, x.base))
    interInDelDao.deleteAll.flatMap(_ => interInDelDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + Utils.getTime(startTime))
    }
  }

}
